---
title: Getting Started
---

import { Callout } from 'nextra/components';

import DocBasic from '../../components/doc-basic';
import DocMotion from '../../components/doc-motion';
import DocCover from '../../components/doc-cover';
import DocToolbar from '../../components/doc-toolbar';
import DocRead from '../../components/doc-read';
import DocButton from '../../components/doc-button';
import DocLoop from '../../components/doc-loop';
import DocBackdrop from '../../components/doc-backdrop';
import DocPure from '../../components/doc-pure';
import DocClose from '../../components/doc-close';
import DocBroken from '../../components/doc-broken';
import DocControlled from '../../components/doc-controlled';
import DocPart from '../../components/doc-part';
import DocOverlay from '../../components/doc-overlay';
import DocRender from '../../components/doc-render';

## Install

Run the following commands in the `React` project directory:

pnpm:

```plaintext
pnpm i react-photo-view
```

yarn:

```plaintext
yarn add react-photo-view
```

or with npm:

```plaintext
npm install react-photo-view
```

## Quick start

Import the `CSS` file at the entry point of the application

```js
import 'react-photo-view/dist/react-photo-view.css';
```

Introduce `PhotoProvider` and `PhotoView` components where they need to be used, basic usage:

```jsx
import { PhotoProvider, PhotoView } from 'react-photo-view';

export default function MyComponent() {
  return (
    <PhotoProvider>
      <div className="foo">
        {images.map((item, index) => (
          <PhotoView key={index} src={item}>
            <img src={item} alt="" />
          </PhotoView>
        ))}
      </div>
    </PhotoProvider>
  );
}
```

With the `PhotoProvider` as the boundary, all the `PhotoView` images in it will be extracted as a set of image preview galleries in the running order. When an `<img >` is clicked, it will locate the specified image and open the preview:

<DocBasic />

If you need the images of the entire page to form a group for preview, you can nest `PhotoProvider` at the entrance of the page.

<Callout emoji="💡">
  Note: `react-photo-view` only keeps three images loaded in the `DOM` at a time, since each image is a compositing
  layer, which consumes quite a bit of memory.
</Callout>

## Animation

### Transition effect

`react-photo-view` uses quite a lot of `CSS3` animations to ensure a smoother experience, and uses spring animations to stay close to the native scrolling effect when scrolling. `CSS3` animations have a default duration of `400ms`, using the `cubic-bezier(0.25, 0.8, 0.25, 1)` animation function.

You can customize the animation time or function by setting `speed` , `easing`:

```jsx
<PhotoProvider
  speed={() => 800}
  easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}
/>
```

`speed` and `easing` are functions. The type of the current animation can be judged by the parameter `type`. The values of `type` are:

- `1` - open
- `2` - closing
- `3` - toggle `index`

<DocMotion />

<Callout emoji="💡">If the child node is not an `img` node, an additional `opacity` fade effect will be added.</Callout>

### Crop Thumbnails

The `<PhotoView>` component automatically recognizes the child node type, and if the child node is `<img >`, and the `object-fit` property is set using `CSS`, it will have a cropping effect in the zoom animation:

```jsx
<PhotoView src={imageURL}>
  <img src={imageURL} style={{ objectFit: 'cover' }} alt="" />
</PhotoView>
```

<DocCover />

## Custom toolbar

Add parameter `toolbarRender` function to `<PhotoProvider>`, which must return a `ReactNode` node.

### Zoom in or zoom out

Add a `scale` control to achieve zoom in and out buttons:

```jsx
<PhotoProvider
  toolbarRender={({ onScale, scale }) => {
    return (
      <>
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale + 1)} />
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale - 1)} />
      </>
    );
  }}
/>
```

### Rotate

`react-photo-view` does not have a built-in rotation control button by default, but provides a rotation `API`. We can achieve the rotation effect by simply adding an `SVG` image:

```jsx
<PhotoProvider
  toolbarRender={({ rotate, onRotate }) => {
    return <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onRotate(rotate + 90)} />;
  }}
/>
```

The parameters provided by the `toolbarRender` function are:

- `images` - list of images `DataType[]`
- `index` - the current index `number`
- `onIndexChange` - index change callback `(index: number) => void`
- `visible` - whether to see `boolean`
- `onClose` - close event callback `() => void`
- `overlayVisible` - whether the overlay is visible `boolean`
- `overlay` - Overlay
- `rotate` - the current rotation angle `number`
- `onRotate` - Rotate event callback `(rotate: number) => void`
- `scale` - the current scale `number`
- `onScale` - Scale event callback `(scale: number) => void`

The following example implements a **rotating full-screen zoom in and out toolbar**:

<DocToolbar />

## Long image mode

When loading images whose aspect ratio exceeds `3` times, the mobile device will enter the long image mode by default. That is, the preview starts at the top of the image, and the width of the image fills the width with the mobile device:

<DocRead />

## Trigger node

`<PhotoView>` child components can be of the regular `React.HTMLAttributes` type, such as `<img>`, `<div>`, etc.

If it is a custom component, you need to ensure that `onClick` event can be triggered normally, and forward `ref` to `HTMLElement` to ensure the correctness of opening and closing the animation source.

```jsx
<PhotoView src={imageURL}>
  <Button primary>Click</Button>
</PhotoView>
```

<DocButton />

## `PhotoSlider`

In general, using `<PhotoProvider>` in combination with `<PhotoView>` already suffices for most needs. For more advanced custom controls, `<PhotoSlider>` can be used.

The parameters of `<PhotoSlider>` are inherited from `<PhotoProvider>`. Several additional APIs are exposed:

- `images` list of images, `DataType[]`
- `visible` whether the controlled property `boolean` is visible
- `onClose` close event callback `() => void`
- `afterClose` callback after closing animation `() => void`
- `index` The current index controlled property `number`
- `onIndexChange` index change callback `(index: number) => void`

`visible` is controlled with `onClose`, and `index` is controlled with `onIndexChange`.

```jsx
export default function MyComponent() {
  const [visible, setVisible] = useState(false);
  const [index, setIndex] = useState(0);

  return (
    <>
      <Button onClick={() => setIndex(2)}>setIndex(2)</Button>
      <Button onClick={() => setIndex(4)}>setIndex(4)</Button>
      <Button onClick={() => setVisible(true)} primary>
        Click
      </Button>

      <PhotoSlider
        images={images.map((item) => ({ src: item, key: item }))}
        visible={visible}
        onClose={() => setVisible(false)}
        index={index}
        onIndexChange={setIndex}
      />
    </>
  );
}
```

<DocControlled />

## Preview more

`<PhotoView >` can also be added to the preview queue without subcomponents, but it cannot directly trigger the preview of the current picture. Through this feature, we can realize the function of previewing more pictures through an entrance:

```jsx
<PhotoProvider>
  {images.map((item, index) => (
    <PhotoView key={index} src={item}>
      {index < 2 ? <img src={item} alt="" /> : undefined}
    </PhotoView>
  ))}
</PhotoProvider>
```

<DocPart />

## Custom render node

`<PhotoView>` has built-in support for image preview by default, just pass `src`, the component will automatically recognize the width and height of the image.

We can also implement the preview of custom nodes through the `render` function to support the needs of `video` or `pdf`.

```jsx
const elementSize = 400;

function MyComponent() {
  return (
    <PhotoView
      width={elementSize}
      height={elementSize}
      render={({ scale, attrs }) => {
        const width = parseFloat((attrs?.style?.width ?? 0) as string);
        const offset = (width - elementSize) / elementSize;
        const childScale = scale === 1 ? scale + offset : 1 + offset;

        return (
          <div {...attrs}>
            <div style={{ transform: `scale(${childScale})`, width: elementSize, transformOrigin: '0 0' }}>
              <div>Hello world</div>
              <Button>button</Button>
              <input onMouseDown={(e) => e.stopPropagation()} />
            </div>
          </div>
        );
      }}
    >
      <Button primary>Click</Button>
    </PhotoView>
  );
}
```

<Callout emoji="💡">
  Note: After the gallery is zoomed, it will first be scaled with `scale`, and after the animation is over, it will be
  adjusted to the width and height corresponding to the current `scale`, and the `scale` will be reset to `1`.
</Callout>

<DocRender />

## PhotoProvider parameter

### Custom description

Add the `overlayRender` function to `<PhotoProvider>` to implement custom nodes. **The return value of the function parameter is the same as that of `toolbarRender`**.

<DocOverlay />

### Loop preview

Add parameter `loop` on `<PhotoProvider>` to change the number of loop previews. Set to `boolean` to enable and disable, set to `number` type to exceed the specific number to enable loop preview, the default value is `3` sheets:

```jsx
<PhotoProvider loop={4} />
```

<DocLoop />

### Mask Transparency

The default is opaque, and the transparency will gradually decrease during the pull-up process. You can add `maskOpacity` to `<PhotoProvider>` to adjust the default transparency, which can be set to a number between `0-1`:

```jsx
<PhotoProvider maskOpacity={0.5} />
```

<DocBackdrop />

### Don't show top button area

Set `bannerVisible` to `false` to hide the top area:

```jsx
<PhotoProvider bannerVisible={false} />
```

<DocPure />

### Masking event

`pullClosable` (default `true`) can close the gallery by pulling down, `maskClosable` (default `true`) can click on the mask to close the gallery:

```jsx
<PhotoProvider pullClosable={false} maskClosable={false} />
```

<DocClose />

## Custom feedback

`<PhotoView>` If loading fails, empty nodes are rendered by default, and you may need to set your own error feedback nodes according to the current UI.

- `brokenElement` - set the load failure pattern `React.ReactElement`
- `loadingElement` - set the loading pattern `React.ReactElement`

<DocBroken />
